package org.easy.http.client.http;

import okhttp3.*;
import okhttp3.internal.Util;
import okhttp3.internal.http.HttpHeaders;
import okio.Buffer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * @Description TODO
 * @Author WuXueLi
 * @Date 18:29 2023/2/25
 * @Version 1.0
 **/
public enum DebugLoggingInterceptor implements Interceptor {

    /**
     *
     */
    INSTANCE;

    private static final Logger LOGGER = LoggerFactory.getLogger(DebugLoggingInterceptor.class);


    private Level loggingLevel;

    DebugLoggingInterceptor() {
        this.loggingLevel =Level.NONE;
    }

    public void setLoggingLevel (Level loggingLevel) {
        this.loggingLevel = loggingLevel == null ? Level.NONE : loggingLevel;
    }

    @Override
    public Response intercept(Chain chain) throws IOException {

        Request request = chain.request();
        loggerRequest(request);

        long startTime = System.nanoTime();
        Response response = chain.proceed(request);
        long usedTime = TimeUnit.NANOSECONDS.toMillis((System.nanoTime() - startTime));
        loggerResponse(response,usedTime);
        return response;
    }

    private void loggerResponse(Response response, long usedTime) throws IOException {
        if (loggingLevel == Level.NONE || loggingLevel == Level.REQUEST) {
            return;
        }
        boolean bodyPrint = loggingLevel == Level.RESPONSE || loggingLevel == Level.BASIC || loggingLevel == Level.ALL,
                headerPrint = loggingLevel == Level.RESPONSE || loggingLevel == Level.HEADER || loggingLevel == Level.ALL;
        HttpUrl httpUrl = response.request().url();
        ResponseBody responseBody = response.peekBody(1024 << 1);

        if (loggingLevel == Level.RESPONSE) {
            LOGGER.info(" |=== Start to print the connection[{}] data ===|", httpUrl.toString());
        }
        LOGGER.info(" |=== The request executes over,http statusCode is {},http message is {},taking {} ms",
                response.code(), response.message(), usedTime);
        if (headerPrint) {
            LOGGER.info(" |=== Start to print response headers === |");
            Headers headers = response.headers();
            for (String name : headers.names()) {
                LOGGER.info(" |=== {} : {} ===|", name, headers.get(name));
            }
            LOGGER.info(" |=== Finish to print response headers === |");
        }
        if (bodyPrint && HttpHeaders.hasBody(response)) {
            if (isPlainText(responseBody.contentType())) {
                LOGGER.info(" |=== Start to print response body === |");
                LOGGER.info(" |=== The body data is \n{}", responseBody.string());
                LOGGER.info(" |=== Finish to print response body === |");
            } else {
                LOGGER.warn(" |=== The response body may contains 'file' part, ignore to print! ===|");
            }
        }
        LOGGER.info(" |=== Finish to print the connection[{}] data ===|\n", httpUrl.toString());


    }

    private void loggerRequest (Request request) throws IOException {
        if (loggingLevel == Level.NONE || loggingLevel == Level.RESPONSE) {
            return;
        }

        boolean bodyPrint = loggingLevel == Level.REQUEST ||
                loggingLevel == Level.BASIC ||
                loggingLevel == Level.ALL,
                headPrint = loggingLevel == Level.REQUEST ||
                        loggingLevel == Level.HEADER ||
                        loggingLevel == Level.ALL;
        HttpUrl httpUrl = request.url();
        LOGGER.info("|=== Start to Print the connection[{}] data === |",httpUrl.toString());
        if (headPrint) {
            LOGGER.info("| === Start to Request Headers === |");
            Headers headers = request.headers();
            for (String name : headers.names()) {
                LOGGER.info("| === {} : {} === |",name,headers.get(name));
            }
            LOGGER.info("| === Finish to Print Request Headers === |");
        }

        if (bodyPrint) {
            RequestBody requestBody = request.body();
            if (Objects.nonNull(requestBody)) {
                if (isPlainText(requestBody.contentType())) {
                    LOGGER.info("| === Start to Print Request Body === |");
                    Request copy = request.newBuilder().build();
                    requestBody = copy.body();
                    Buffer buffer = new Buffer();
                    assert requestBody != null;
                    requestBody.writeTo(buffer);
                    MediaType mediaType = requestBody.contentType();
                    assert mediaType != null;
                    LOGGER.info(" |=== The body data is {} ===|", buffer.readString(Objects.requireNonNull(mediaType.charset(Util.UTF_8))));
                    LOGGER.info(" |=== Finish to print request body === |");
                } else {
                    LOGGER.warn("|=== The Request Body May Contains 'file' part, ignore to Print! ===|");
                }
            }
        }

    }

    private boolean isPlainText (MediaType mediaType) {
        if (Objects.isNull(mediaType)) {
            return Boolean.FALSE;
        }

        if (Objects.nonNull(mediaType.type()) && "text".equals(mediaType.type())) {
            return Boolean.TRUE;
        }

        String subType = mediaType.subtype();
        if (Objects.nonNull(subType)) {
            if (subType.contains("x-www-form-urlencoded") ||
                    subType.contains("json") ||
                    subType.contains("xml") ||
                    subType.contains("html")) {
                return Boolean.TRUE;
            }
        }
        return Boolean.FALSE;
    }


    public enum Level {

        /**
         * 不打印日志
         */
        NONE,

        /**
         * 打印请求参数和响应结果
         */
        BASIC,

        /**
         * 打印请求Header和响应Header
         */
        HEADER,

        /**
         * 打印请求参数和header
         */
        REQUEST,

        /**
         * 打印响应结果和header
         */
        RESPONSE,

        /**
         * 打印所有信息
         */
        ALL,
    }
}
